#include "mypaint3d.h"
#include <GL/glu.h>
#include <QOpenGLFunctions>
#include <QString>

MyPaint3D::MyPaint3D(QWidget *parent)
    : QOpenGLWidget{parent}
{
    m_Rotate = Point3d(0, -70, 0);
    //坐标系Z轴方向偏移量
    m_scloe = -15;
    m_txtTool = new CPaint2DTextTool(3, false);
    colorType = COLOR_DEFULT;
    m_drawType = DrawVertical;

    m_Size = Point3d(8, 2, 6);

    m_xyGridNum[0] = m_Size.y;
    m_xyGridNum[1] = m_Size.x;
    m_yzGridNum[0] = m_Size.y;
    m_yzGridNum[1] = m_Size.z;

    maxPeriod = 100;
    //XYZ轴交汇原点偏移量，使偏移量是轴长的一半(正负代表方向)，是为了使实际原点在坐标系的中心(空间)
    m_translate = Point3d(-m_Size.x/2.0f,-m_Size.y/2.0f,-m_Size.z/2.0f);

    m_time = 0;
    waveNum = 50;
    timeInterval = 1;

    //设置多采样的值 抗锯齿
    QSurfaceFormat fmt = format();
    fmt.setSamples(18);
    setFormat(fmt);

    m_datas = NULL;
    m_colorScole = 0;
    m_valScole.y = 1;
    m_valScole.x = m_Size.x / maxPeriod;
    m_valScole.z = m_Size.z / waveNum;

    yMax = 0;
    m_bgColor = QColor(0, 51, 76);
}

MyPaint3D::~MyPaint3D()
{
    if(m_datas != NULL)
    {
        for(int i =0; i< waveNum;i++)
        {
            delete m_datas[i];
        }
        delete m_datas;
        m_datas = NULL;
    }
    if (m_txtTool != NULL)
    {
        delete m_txtTool;
        m_txtTool = NULL;
    }
}

void MyPaint3D::setDataToObj(float *data, int begin, int end)
{
    CDataCell cell;
    if(colorType == COLOR_SELECTOR)
    {
        for(int i = begin; i < end; i++)
        {
            cell.val = data[i];
            cell.curTime = m_time;
            m_colorSelector.valToColor(data[i], cell.mColor[0], cell.mColor[1], cell.mColor[2]);
            m_datas[i]->setData(cell);
        }
    }
    else if(colorType == COLOR_RANDOM)
    {
        for(int i = begin; i < end; i++)
        {
            cell.val = data[i];
            cell.curTime = m_time;
            cell.mColor[0] = m_colorScole * data[i];
            m_datas[i]->setData(cell);
        }
    }
    else
    {
        for(int i = begin; i < end; i++)
        {
            cell.val = data[i];
            cell.curTime = m_time;
            m_datas[i]->setData(cell);
        }
    }
}

void MyPaint3D::setData(float *data)
{
    m_time += timeInterval;
    setDataToObj(data, 0, waveNum);
    update();
}

void MyPaint3D::setSomeData(float *data, int num)
{
    m_time += timeInterval;
    if (num < waveNum)
    {
        setDataToObj(data, 0, num);
        CDataCell cell;
        cell.isPaint = false;
        cell.curTime = m_time;
        for(int i = num; i < waveNum; i++)
        {
            m_datas[i]->setData(cell);
        }
    }
    else
    {
        setDataToObj(data, 0, waveNum);
    }
    update();
}

MyPaint3D* MyPaint3D::initBuffer(int num, int periodNum, float yVal)
{
    if (waveNum != num)
    {
        //删除在重新分配
        if(m_datas != NULL)
        {
            for(int i =0; i< waveNum;i++)
            {
                delete m_datas[i];
            }
            delete m_datas;
            m_datas = NULL;
        }
    }
    waveNum = num;
    maxPeriod = periodNum;
    m_valScole.x = m_Size.x / maxPeriod;
    m_valScole.z = m_Size.z / waveNum;
    if(m_datas == NULL)
    {
        //重新分配内存
        m_datas = new CData*[waveNum];
        for(int i =0; i < waveNum; i++)
        {
            m_datas[i] = new CData(periodNum);
        }
    }
    yMax = yVal;
    return this;
}

MyPaint3D* MyPaint3D::setY(float size)
{
    m_Size.y = size;
    m_valScole.y = size / yMax;
    m_translate.y = -m_Size.y / 2.0f;
    return this;
}

MyPaint3D* MyPaint3D::setX(float size)
{
    m_Size.x = size;
    m_valScole.x = size / maxPeriod;
    m_translate.x = -m_Size.x / 2.0f;
    return this;
}

MyPaint3D* MyPaint3D::setZ(float size)
{
    m_Size.z = size;
    m_valScole.z = size / waveNum;
    m_translate.z = - m_Size.z / 2.0f;
    return this;
}

MyPaint3D *MyPaint3D::setColorType(LineType t)
{
    colorType = t;
    return this;
}

MyPaint3D *MyPaint3D::setDrawType(DrawType t)
{
    m_drawType = t;
    return this;
}

MyPaint3D* MyPaint3D::setColorMax(float max)
{
    m_colorScole = 1.0 / (max + 10);
    return this;
}

MyPaint3D *MyPaint3D::setColorSelector(float min, float max, byte r, byte g, byte b)
{
    m_colorSelector.set(min, max, r, g, b);
    return this;
}

MyPaint3D *MyPaint3D::setGridNum(int xyHGridNum, int xyVGridNum, int yzHGridNum, int yzVGridNum)
{
    m_xyGridNum[0] = xyHGridNum;
    m_xyGridNum[1] = xyVGridNum;
    m_yzGridNum[0] = yzHGridNum;
    m_yzGridNum[1] = yzVGridNum;
    return this;
}

MyPaint3D *MyPaint3D::setXText(QString name, QFont &font, QColor color)
{
    m_txtTool->setText(0, name, font, color);
    return this;
}

MyPaint3D *MyPaint3D::setYText(QString name, QFont &font, QColor color)
{
    m_txtTool->setText(1, name, font, color);
    return this;
}

MyPaint3D *MyPaint3D::setZText(QString name, QFont &font, QColor color)
{
    m_txtTool->setText(2, name, font, color);
    return this;
}

MyPaint3D *MyPaint3D::setXText(QString filePath)
{
    m_txtTool->setText(0, filePath);
    m_txtTool->resizeSize(0, 25, 25);
    return this;
}

MyPaint3D *MyPaint3D::setYText(QString filePath)
{
    m_txtTool->setText(1, filePath);
    m_txtTool->resizeSize(1, 25, 25);
    return this;
}

MyPaint3D *MyPaint3D::setZText(QString filePath)
{
    m_txtTool->setText(2, filePath);
    m_txtTool->resizeSize(2, 25, 25);
    return this;
}

MyPaint3D *MyPaint3D::setBackground(QColor color)
{
    m_bgColor = color;
    return this;
}

MyPaint3D *MyPaint3D::setRotate(int xAngel, int yAngel, int zAngel)
{
    m_Rotate.x = xAngel;
    m_Rotate.y = yAngel;
    m_Rotate.z = zAngel;
    return this;
}

void MyPaint3D::initializeGL()
{
    initializeOpenGLFunctions();
    glClearColor(m_bgColor.redF(), m_bgColor.greenF(), m_bgColor.blueF(), 1.0);
    glShadeModel(GL_SMOOTH);
    glClearDepth(1.0);
    //开启深度检测
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    m_txtTool->initShaderProgram();
    m_txtTool->createTextures();
}


void MyPaint3D::resizeGL(int w, int h)
{
    //防止height为0
    if (h == 0) h = 1;
    //重置当前的视口
    glViewport(0, 0,(GLint)w, (GLint)h);
    //选择投影矩阵
    glMatrixMode(GL_PROJECTION);
    //重置投影矩阵
    glLoadIdentity();
    //建立透视投影矩阵,基于窗口宽度和高度的m_isize 45度视角来计算
    gluPerspective(45.0, (GLfloat)w / (GLfloat)h, 0.01, 100.0);
    //选择模型观察矩阵
    glMatrixMode(GL_MODELVIEW);
    //重置模型观察矩阵
    glLoadIdentity();
}

void MyPaint3D::paintGL()
{
    //清除屏幕和深度缓存
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    //重置当前的模型观察矩阵 在实际绘制之前，需要调用glLoadIdentity()来清除当前的矩阵
    glLoadIdentity();

    // 坐标系放大缩小就是修改该变量
    glTranslatef(0, 0, m_scloe);
    //绕原点X轴旋转， 通过修改m_rot1坐标系上下偏转角度
    glRotatef(abs(m_rot1.z), m_rot1.x, 0, 0);
    //绕原点Y轴旋转， 通过修改m_rot2坐标系左右偏转角度
    glRotatef(abs(m_rot2.z), 0, m_rot2.y, 0);
    if (m_Rotate.y != 0) {
        glRotatef(m_Rotate.y, 0, 1, 0);
    }
    if (m_Rotate.x != 0) {
        glRotatef(m_Rotate.x, 1, 0, 0);
    }
    if (m_Rotate.z != 0) {
        glRotatef(m_Rotate.z, 0, 0, 1);
    }

    paintData();
    painAxis();
    paintXYZText();
    glFlush();
}
void MyPaint3D::paintData()
{
    if(m_datas == NULL)
    {
        return;
    }
    CData* tmp = NULL;
    CDataCell* cell = NULL;
    glPushMatrix();
    {
        glTranslatef(m_translate.x, m_translate.y, m_translate.z);
        if (m_drawType == DrawVertical)
        {
            glLineWidth(0.4);
            for (int n = 0; n < waveNum; n++)
            {
                tmp = m_datas[n];
                if (tmp == NULL)
                {
                    continue;
                }
                long offset = tmp->getOffset();
                double x =  0;
                double z = m_valScole.z * n;
                //按照顺序画就好了，不需要从head开始画
                for(int j = 0; j < tmp->Count(); j++)
                {
                    cell = tmp->datas[j];
                    if (cell->isPaint)
                    {
                        glColor3f(cell->mColor[0], cell->mColor[1], cell->mColor[2]);
                        x = (cell->curTime - offset) * m_valScole.x;
                        glBegin(GL_LINE_STRIP);
                        glVertex3f(x, 0, z);
                        glVertex3f(x, cell->val * m_valScole.y, z);
                        glEnd();
                    }
                }
                glFlush();

                // glBegin(GL_LINE_STRIP);
                // //按照顺序画就好了，不需要从head开始画
                // for(int j = 0; j < tmp->Count(); j++)
                // {
                //     cell = tmp->datas[j];
                //     if (cell->isPaint)
                //     {
                //         glColor3f(cell->mColor[0], cell->mColor[1], cell->mColor[2]);
                //         x = (cell->curTime - offset) * m_valScole.x;

                //         //glEnable(GL_LINE_SMOOTH);
                //         glVertex3f(x, 0, z);
                //         glVertex3f(x, cell->val * m_valScole.y, z);
                //         glVertex3f(x, 0, z);
                //     }
                // }
                // glEnd();
                // glFlush();
            }
        }
        else if(m_drawType == DrawLine)
        {
            glLineWidth(0.4);
            for (int n = 0; n < waveNum; n++)
            {
                tmp = m_datas[n];
                if (tmp == NULL)
                {
                    continue;
                }
                long offset = tmp->getOffset();
                double z = m_valScole.z * n;
                glBegin(GL_LINE_STRIP);

                //head和tail解决，要考虑顺序
                if (tmp->Head() <= tmp->Tail())
                {
                    for(int j = 0; j < tmp->Tail(); j++)
                    {
                        cell = tmp->datas[j];
                        if (cell->isPaint)
                        {
                            glColor3f(cell->mColor[0], cell->mColor[1], cell->mColor[2]);
                            glVertex3f((cell->curTime - offset) * m_valScole.x, cell->val * m_valScole.y, z);
                        }
                    }
                }
                else
                {
                    for(int j = tmp->Head(); j < tmp->Count(); j++)
                    {
                        cell = tmp->datas[j];
                        if (cell->isPaint)
                        {
                            glColor3f(cell->mColor[0], cell->mColor[1], cell->mColor[2]);
                            glVertex3f((cell->curTime - offset) * m_valScole.x, cell->val * m_valScole.y, z);
                        }
                    }
                    for(int j = 0; j < tmp->Tail(); j++)
                    {
                        cell = tmp->datas[j];
                        if (cell->isPaint)
                        {
                            glColor3f(cell->mColor[0], cell->mColor[1], cell->mColor[2]);
                            glVertex3f((cell->curTime - offset) * m_valScole.x, cell->val * m_valScole.y, z);
                        }
                    }
                }
                glEnd();
                glFlush();
            }
        }
        else if(m_drawType == DrawPoints)
        {
            glPointSize(0.5);
            for (int n = 0; n < waveNum; n++)
            {
                tmp = m_datas[n];
                if (tmp == NULL)
                {
                    continue;
                }
                long offset = tmp->getOffset();
                double z = m_valScole.z * n;
                glBegin(GL_POINTS);
                for(int j = 0; j < tmp->Count(); j++)
                {
                    cell = tmp->datas[j];
                    if (cell->isPaint)
                    {
                        glColor3f(cell->mColor[0], cell->mColor[1], cell->mColor[2]);
                        glVertex3f((cell->curTime - offset) * m_valScole.x, cell->val * m_valScole.y, z);
                    }
                }
                glEnd();
                glFlush();
            }
        }
    }
    glPopMatrix();
}

void MyPaint3D::painAxis()
{
    glPushMatrix();
    {
        glLineWidth(1.5);
        //沿x,y,z轴方向的平移
        glTranslatef(m_translate.x, m_translate.y, m_translate.z);
        glBegin(GL_LINES);
        {
            glEnable(GL_LINE_SMOOTH);
            glColor3f(1, 0, 0); glVertex3f(0, 0, 0); glVertex3f(m_Size.x, 0, 0); //X
            glColor3f(0, 1, 0); glVertex3f(0, 0, 0); glVertex3f(0, m_Size.y, 0); //Y
            glColor3f(0, 0, 1); glVertex3f(0, 0, 0); glVertex3f(0, 0, m_Size.z); //Z
        }
        glEnd();
        //绘制网格
        glColor3f(1, 1, 1);
        glLineWidth(1.0f);
        paintXYGrid(m_xyGridNum[0], m_xyGridNum[1]);
        paintYZGrid(m_yzGridNum[0], m_yzGridNum[1]);
    }
    glPopMatrix();

    // 坐标轴箭头
    GLUquadricObj *objCylinder = gluNewQuadric();
    glPushMatrix(); //X
    {
        glColor3f(1, 0, 0);
        glTranslatef(m_translate.x + m_Size.x, m_translate.y, m_translate.z);
        glRotatef(90, 0, 1, 0); //按照y轴旋转90
        gluCylinder(objCylinder, 0.06, 0.0, 0.4, 50, 1);
    }
    glPopMatrix();

    glPushMatrix(); //Y
    {
        glColor3f(0, 1, 0);
        glTranslatef(m_translate.x, m_translate.y+m_Size.y, m_translate.z);
        glRotatef(-90, 1, 0, 0.0);
        gluCylinder(objCylinder, 0.06, 0.0, 0.4, 50, 1);
    }
    glPopMatrix();

    glPushMatrix(); //Z
    {
        glColor3f(0, 0, 1);
        glTranslatef(m_translate.x, m_translate.y, m_translate.z+m_Size.z);
        gluCylinder(objCylinder, 0.06, 0.0, 0.4, 50, 1);
    }
    glPopMatrix();


    // glPushMatrix();
    // {
    //     glTranslatef(m_translate.x, m_translate.y, m_translate.z);

    //     // 画原点 - 白色
    //     glPointSize(2.5);
    //     glBegin(GL_POINTS);
    //     {
    //         glColor3f(1, 1, 1);
    //         glVertex3f(0, 0, 0);
    //         //painTicks(20, EX);
    //     }
    //     glEnd();
    //     glFlush();
    // }
    // glPopMatrix();

    //paintGlText(Point3d(m_Size.x + 0.1, 0, 0.2), QColor(255, 0, 0), "时间");
    //paintGlText(Point3d(-0.1, m_Size.y + 0.6, 0.1), QColor(0, 255, 0), "振幅");

    //QString::number(start, 'G', 5);
    // glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
    // paintQtText(100, 100, "时间");
}

void MyPaint3D::paintGrid(Point3d start, Point3d end, int num)
{
    float xSpacing = (end.x - start.x) / num;
    float ySpacing = (end.y - start.y) / num;
    float zSpacing = (end.z - start.z) / num;

    glLineWidth(0.1f);
    glLineStipple(1, 0x0303);//线条样式
    //glColor3f(1, 0, 0)
    glBegin(GL_LINES);
    glEnable(GL_LINE_SMOOTH);
    for(int i = 0; i < num; ++i)
    {
        float x = start.x + xSpacing * i;
        float y = start.y + ySpacing * i;
        float z = start.z + zSpacing * i;

        // 横线
        glVertex3f(start.x, y, z);
        glVertex3f(end.x, y, z);
        // 竖线
        glVertex3f(x, start.y, z);
        glVertex3f(x, end.y, z);
        // 垂直于 横线 与 竖线 所在平面
        glVertex3f(x, y, start.z);
        glVertex3f(x, y, end.z);
    }
    glEnd();
}

void MyPaint3D::paintXYGrid(int hNum, int vNum)
{
    float xSpacing = m_Size.x / vNum;
    float ySpacing = m_Size.y / hNum;
    glBegin(GL_LINES);
    glEnable(GL_LINE_SMOOTH);
    for(int i = 1; i <= hNum; ++i)
    {
        float y = ySpacing * i;

        glVertex3f(0, y, 0);
        glVertex3f(m_Size.x, y, 0);
    }
    for(int i = 1; i <= vNum; ++i)
    {
        float x = xSpacing * i;
        glVertex3f(x, 0, 0);
        glVertex3f(x, m_Size.y, 0);
    }
    glEnd();
}

void MyPaint3D::paintYZGrid(int hNum, int vNum)
{
    float zSpacing = m_Size.z / vNum;
    float ySpacing = m_Size.y / hNum;
    glBegin(GL_LINES);
    glEnable(GL_LINE_SMOOTH);
    for(int i = 1; i <= hNum; ++i)
    {
        float y = ySpacing * i;

        glVertex3f(0, y, 0);
        glVertex3f(0, y, m_Size.z);
    }
    for(int i = 1; i <= vNum; ++i)
    {
        float z = zSpacing * i;
        glVertex3f(0, 0, z);
        glVertex3f(0, m_Size.y, z);
    }
    glEnd();
}

void MyPaint3D::painTicks(int num, Axis type)
{
    float step = 0.f;
    if (num <= 0)
    {
        return;
    }

    if (type == EX) // x轴
    {
        step = m_Size.x / num;
        for(int i = 1; i < num; i++)
        {
            glVertex3f(step * i, 0, 0);
        }
    }
    else if (type == EY) // y轴
    {
        step = m_Size.y / num;
        for(int i = 1; i < num; i++)
        {
            glVertex3f(0, step * i, 0);
        }
    }
    else if (type == EZ) //轴
    {
        step = m_Size.z / num;
        for(int i = 1; i < num; i++)
        {
            glVertex3f(0, 0, step * i);
        }

    }
}

void MyPaint3D::paintXYZText()
{
    static GLdouble modelview[16];
    static GLdouble projection[16];
    static GLint viewport[4];
    static GLdouble wx, wy, wz;
    glPushMatrix();
    {
        //去除文字黑色背景
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glTranslatef(m_translate.x, m_translate.y, m_translate.z);

        //求绘制的三维坐标， 然后投影到屏幕
        glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
        glGetDoublev(GL_PROJECTION_MATRIX, projection);
        glGetIntegerv(GL_VIEWPORT, viewport);
        gluProject(m_Size.x+0.1, -0.1, -0.1, modelview, projection, viewport, &wx, &wy, &wz);
        m_txtTool->drawTexture(0, wx, wy, 0, -5);

        gluProject(-0.01, m_Size.y+0.4, -0.01, modelview, projection, viewport, &wx, &wy, &wz);
        m_txtTool->drawTexture(1, wx, wy);

        gluProject(-0.1, -0.1, m_Size.z+0.1, modelview, projection, viewport, &wx, &wy, &wz);
        m_txtTool->drawTexture(2, wx, wy, 0, -5);
    }
    glPopMatrix();
    //恢复视口
    //glViewport(0, 0, this->width(), this->height());
}

void MyPaint3D::mousePressEvent(QMouseEvent *event)
{
    QOpenGLWidget::mousePressEvent(event);

    QPoint pos = event->pos();
    static GLdouble modelview[16];
    static GLdouble projection[16];
    static GLint viewport[4];
    static GLdouble wX, wY, wZ;

    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
    glGetDoublev(GL_PROJECTION_MATRIX, projection);
    glGetIntegerv(GL_VIEWPORT, viewport);

    // 将鼠标坐标转换为世界坐标
    gluUnProject(pos.x(), pos.y(), 0, modelview, projection, viewport, &wX, &wY, &wZ);
    m_press.x = wX;
    m_press.y = wY;
    m_press.z = 0;
}

void MyPaint3D::mouseReleaseEvent(QMouseEvent *event)
{
    QOpenGLWidget::mouseReleaseEvent(event);
    m_press.x = 0;
    m_press.y = 0;
    m_press.z = 0;
}

void MyPaint3D::mouseMoveEvent(QMouseEvent *event)
{
    QOpenGLWidget::mouseMoveEvent(event);

    QPoint pos = event->pos();

    static GLdouble modelview[16];
    static GLdouble projection[16];
    static GLint viewport[4];
    static GLdouble wx, wy, wz;

    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
    glGetDoublev(GL_PROJECTION_MATRIX, projection);
    glGetIntegerv(GL_VIEWPORT, viewport);
    // 将鼠标坐标转换为世界坐标
    gluUnProject(pos.x(), pos.y(), 0, modelview, projection, viewport, &wx, &wy, &wz);

    if(event->buttons() & Qt::LeftButton)
    {
        //上下旋转， 绕X轴，计算的鼠标移动值(不是旋转角度)
        //只要m_rot.x不为0，就绕x旋转
        // glRotatef(abs(m_rot1.z), m_rot1.x, 0, 0);
        // m_rot1.z = m_rot1.x的作用是保持旋转的角度与实际旋转方向一致，能这么做的原因是：只要m_rot.x不为0，就绕x旋转，正负值控制方向(因此角度值z取绝对值)
        m_rot1.x += (wy-m_press.y)*100;
        m_rot1.z = m_rot1.x;
        //左右旋转， 绕Y轴（将 || 改为 && 即可让鼠标左键控制上下翻转，右键控制左右翻转）
        m_rot2.y += (wx-m_press.x)*100;
        m_rot2.z = m_rot2.y;
    }

    if(event->buttons() & Qt::RightButton)
    {
        m_translate.x += (wx-m_press.x)*10.0;
        m_translate.y += -(wy-m_press.y)*10.0;
    }

    m_press.x = wx;
    m_press.y = wy;
    m_press.z = 0;
    update();
}

void MyPaint3D::wheelEvent(QWheelEvent *event)
{
    QOpenGLWidget::wheelEvent(event);

    if (event->angleDelta().y() > 0) {
        m_scloe += m_scloe < -1 ? 1 : 0;

    } else if (event->angleDelta().y() < 0) {
        m_scloe -= m_scloe > -100 ? 1 : 0;
    }

    update();
}

